/**
 * vue-router v2.0.0
 * (c) 2016 Evan You
 * @license MIT
 */
(function (global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
    typeof define === 'function' && define.amd ? define(factory) :
    (global.VueRouter = factory());
  }(this, (function () { 'use strict';
  
  var View = {
    name: 'router-view',
    functional: true,
    props: {
      name: {
        type: String,
        default: 'default'
      }
    },
    render: function render (h, ref) {
      var props = ref.props;
      var children = ref.children;
      var parent = ref.parent;
      var data = ref.data;
  
      data.routerView = true
  
      var route = parent.$route
      var cache = parent._routerViewCache || (parent._routerViewCache = {})
      var depth = 0
      var inactive = false
  
      while (parent) {
        if (parent.$vnode && parent.$vnode.data.routerView) {
          depth++
        }
        if (parent._inactive) {
          inactive = true
        }
        parent = parent.$parent
      }
  
      data.routerViewDepth = depth
      var matched = route.matched[depth]
      if (!matched) {
        return h()
      }
  
      var component = inactive
        ? cache[props.name]
        : (cache[props.name] = matched.components[props.name])
  
      if (!inactive) {
        (data.hook || (data.hook = {})).init = function (vnode) {
          matched.instances[props.name] = vnode.child
        }
      }
  
      return h(component, data, children)
    }
  }
  
  /*  */
  
  function resolvePath (
    relative,
    base,
    append
  ) {
    if (relative.charAt(0) === '/') {
      return relative
    }
  
    if (relative.charAt(0) === '?' || relative.charAt(0) === '#') {
      return base + relative
    }
  
    var stack = base.split('/')
  
    // remove trailing segment if:
    // - not appending
    // - appending to trailing slash (last segment is empty)
    if (!append || !stack[stack.length - 1]) {
      stack.pop()
    }
  
    // resolve relative path
    var segments = relative.replace(/^\//, '').split('/')
    for (var i = 0; i < segments.length; i++) {
      var segment = segments[i]
      if (segment === '.') {
        continue
      } else if (segment === '..') {
        stack.pop()
      } else {
        stack.push(segment)
      }
    }
  
    // ensure leading slash
    if (stack[0] !== '') {
      stack.unshift('')
    }
  
    return stack.join('/')
  }
  
  function parsePath (path) {
    var hash = ''
    var query = ''
  
    var hashIndex = path.indexOf('#')
    if (hashIndex >= 0) {
      hash = path.slice(hashIndex)
      path = path.slice(0, hashIndex)
    }
  
    var queryIndex = path.indexOf('?')
    if (queryIndex >= 0) {
      query = path.slice(queryIndex + 1)
      path = path.slice(0, queryIndex)
    }
  
    return {
      path: path,
      query: query,
      hash: hash
    }
  }
  
  function cleanPath (path) {
    return path.replace(/\/\//g, '/')
  }
  
  /*  */
  
  function assert (condition, message) {
    if (!condition) {
      throw new Error(("[vue-router] " + message))
    }
  }
  
  function warn (condition, message) {
    if (!condition) {
      typeof console !== 'undefined' && console.warn(("[vue-router] " + message))
    }
  }
  
  /*  */
  
  var encode = encodeURIComponent
  var decode = decodeURIComponent
  
  function resolveQuery (
    query,
    extraQuery
  ) {
    if ( extraQuery === void 0 ) extraQuery = {};
  
    if (query) {
      var parsedQuery
      try {
        parsedQuery = parseQuery(query)
      } catch (e) {
        warn(false, e.message)
        parsedQuery = {}
      }
      for (var key in extraQuery) {
        parsedQuery[key] = extraQuery[key]
      }
      return parsedQuery
    } else {
      return extraQuery
    }
  }
  
  function parseQuery (query) {
    var res = Object.create(null)
  
    query = query.trim().replace(/^(\?|#|&)/, '')
  
    if (!query) {
      return res
    }
  
    query.split('&').forEach(function (param) {
      var parts = param.replace(/\+/g, ' ').split('=')
      var key = decode(parts.shift())
      var val = parts.length > 0
        ? decode(parts.join('='))
        : null
  
      if (res[key] === undefined) {
        res[key] = val
      } else if (Array.isArray(res[key])) {
        res[key].push(val)
      } else {
        res[key] = [res[key], val]
      }
    })
  
    return res
  }
  
  function stringifyQuery (obj) {
    var res = obj ? Object.keys(obj).sort().map(function (key) {
      var val = obj[key]
  
      if (val === undefined) {
        return ''
      }
  
      if (val === null) {
        return encode(key)
      }
  
      if (Array.isArray(val)) {
        var result = []
        val.slice().forEach(function (val2) {
          if (val2 === undefined) {
            return
          }
          if (val2 === null) {
            result.push(encode(key))
          } else {
            result.push(encode(key) + '=' + encode(val2))
          }
        })
        return result.join('&')
      }
  
      return encode(key) + '=' + encode(val)
    }).filter(function (x) { return x.length > 0; }).join('&') : null
    return res ? ("?" + res) : ''
  }
  
  /*  */
  
  function createRoute (
    record,
    location,
    redirectedFrom
  ) {
    var route = {
      name: location.name || (record && record.name),
      meta: (record && record.meta) || {},
      path: location.path || '/',
      hash: location.hash || '',
      query: location.query || {},
      params: location.params || {},
      fullPath: getFullPath(location),
      matched: record ? formatMatch(record) : []
    }
    if (redirectedFrom) {
      route.redirectedFrom = getFullPath(redirectedFrom)
    }
    return Object.freeze(route)
  }
  
  // the starting route that represents the initial state
  var START = createRoute(null, {
    path: '/'
  })
  
  function formatMatch (record) {
    var res = []
    while (record) {
      res.unshift(record)
      record = record.parent
    }
    return res
  }
  
  function getFullPath (ref) {
    var path = ref.path;
    var query = ref.query; if ( query === void 0 ) query = {};
    var hash = ref.hash; if ( hash === void 0 ) hash = '';
  
    return (path || '/') + stringifyQuery(query) + hash
  }
  
  var trailingSlashRE = /\/$/
  function isSameRoute (a, b) {
    if (b === START) {
      return a === b
    } else if (!b) {
      return false
    } else if (a.path && b.path) {
      return (
        a.path.replace(trailingSlashRE, '') === b.path.replace(trailingSlashRE, '') &&
        a.hash === b.hash &&
        isObjectEqual(a.query, b.query)
      )
    } else if (a.name && b.name) {
      return (
        a.name === b.name &&
        a.hash === b.hash &&
        isObjectEqual(a.query, b.query) &&
        isObjectEqual(a.params, b.params)
      )
    } else {
      return false
    }
  }
  
  function isObjectEqual (a, b) {
    if ( a === void 0 ) a = {};
    if ( b === void 0 ) b = {};
  
    var aKeys = Object.keys(a)
    var bKeys = Object.keys(b)
    if (aKeys.length !== bKeys.length) {
      return false
    }
    return aKeys.every(function (key) { return String(a[key]) === String(b[key]); })
  }
  
  function isIncludedRoute (current, target) {
    return (
      current.path.indexOf(target.path) === 0 &&
      (!target.hash || current.hash === target.hash) &&
      queryIncludes(current.query, target.query)
    )
  }
  
  function queryIncludes (current, target) {
    for (var key in target) {
      if (!(key in current)) {
        return false
      }
    }
    return true
  }
  
  /*  */
  
  function normalizeLocation (
    raw,
    current,
    append
  ) {
    var next = typeof raw === 'string' ? { path: raw } : raw
    if (next.name || next._normalized) {
      return next
    }
  
    var parsedPath = parsePath(next.path || '')
    var basePath = (current && current.path) || '/'
    var path = parsedPath.path
      ? resolvePath(parsedPath.path, basePath, append)
      : (current && current.path) || '/'
    var query = resolveQuery(parsedPath.query, next.query)
    var hash = next.hash || parsedPath.hash
    if (hash && hash.charAt(0) !== '#') {
      hash = "#" + hash
    }
  
    return {
      _normalized: true,
      path: path,
      query: query,
      hash: hash
    }
  }
  
  /*  */
  
  // work around weird flow bug
  var toTypes = [String, Object]
  
  var Link = {
    name: 'router-link',
    props: {
      to: {
        type: toTypes,
        required: true
      },
      tag: {
        type: String,
        default: 'a'
      },
      exact: Boolean,
      append: Boolean,
      replace: Boolean,
      activeClass: String
    },
    render: function render (h) {
      var this$1 = this;
  
      var router = this.$router
      var current = this.$route
      var to = normalizeLocation(this.to, current, this.append)
      var resolved = router.match(to)
      var fullPath = resolved.redirectedFrom || resolved.fullPath
      var base = router.history.base
      var href = base ? cleanPath(base + fullPath) : fullPath
      var classes = {}
      var activeClass = this.activeClass || router.options.linkActiveClass || 'router-link-active'
      var compareTarget = to.path ? createRoute(null, to) : resolved
      classes[activeClass] = this.exact
        ? isSameRoute(current, compareTarget)
        : isIncludedRoute(current, compareTarget)
  
      var on = {
        click: function (e) {
          e.preventDefault()
          if (this$1.replace) {
            router.replace(to)
          } else {
            router.push(to)
          }
        }
      }
  
      var data = {
        class: classes
      }
  
      if (this.tag === 'a') {
        data.on = on
        data.attrs = { href: href }
      } else {
        // find the first <a> child and apply listener and href
        var a = findAnchor(this.$slots.default)
        if (a) {
          var aData = a.data || (a.data = {})
          aData.on = on
          var aAttrs = aData.attrs || (aData.attrs = {})
          aAttrs.href = href
        }
      }
  
      return h(this.tag, data, this.$slots.default)
    }
  }
  
  function findAnchor (children) {
    if (children) {
      var child
      for (var i = 0; i < children.length; i++) {
        child = children[i]
        if (child.tag === 'a') {
          return child
        }
        if (child.children && (child = findAnchor(child.children))) {
          return child
        }
      }
    }
  }
  
  function install (Vue) {
    if (install.installed) { return }
    install.installed = true
  
    Object.defineProperty(Vue.prototype, '$router', {
      get: function get () { return this.$root._router }
    })
  
    Object.defineProperty(Vue.prototype, '$route', {
      get: function get$1 () { return this.$root._route }
    })
  
    Vue.mixin({
      beforeCreate: function beforeCreate () {
        if (this.$options.router) {
          this._router = this.$options.router
          this._router.init(this)
          Vue.util.defineReactive(this, '_route', this._router.history.current)
        }
      }
    })
  
    Vue.component('router-view', View)
    Vue.component('router-link', Link)
  }
  
  var __moduleExports = Array.isArray || function (arr) {
    return Object.prototype.toString.call(arr) == '[object Array]';
  };
  
  var isarray = __moduleExports
  
  /**
   * Expose `pathToRegexp`.
   */
  var index = pathToRegexp
  var parse_1 = parse
  var compile_1 = compile
  var tokensToFunction_1 = tokensToFunction
  var tokensToRegExp_1 = tokensToRegExp
  
  /**
   * The main path matching regexp utility.
   *
   * @type {RegExp}
   */
  var PATH_REGEXP = new RegExp([
    // Match escaped characters that would otherwise appear in future matches.
    // This allows the user to escape special characters that won't transform.
    '(\\\\.)',
    // Match Express-style parameters and un-named parameters with a prefix
    // and optional suffixes. Matches appear as:
    //
    // "/:test(\\d+)?" => ["/", "test", "\d+", undefined, "?", undefined]
    // "/route(\\d+)"  => [undefined, undefined, undefined, "\d+", undefined, undefined]
    // "/*"            => ["/", undefined, undefined, undefined, undefined, "*"]
    '([\\/.])?(?:(?:\\:(\\w+)(?:\\(((?:\\\\.|[^\\\\()])+)\\))?|\\(((?:\\\\.|[^\\\\()])+)\\))([+*?])?|(\\*))'
  ].join('|'), 'g')
  
  /**
   * Parse a string for the raw tokens.
   *
   * @param  {string} str
   * @return {!Array}
   */
  function parse (str) {
    var tokens = []
    var key = 0
    var index = 0
    var path = ''
    var res
  
    while ((res = PATH_REGEXP.exec(str)) != null) {
      var m = res[0]
      var escaped = res[1]
      var offset = res.index
      path += str.slice(index, offset)
      index = offset + m.length
  
      // Ignore already escaped sequences.
      if (escaped) {
        path += escaped[1]
        continue
      }
  
      var next = str[index]
      var prefix = res[2]
      var name = res[3]
      var capture = res[4]
      var group = res[5]
      var modifier = res[6]
      var asterisk = res[7]
  
      // Push the current path onto the tokens.
      if (path) {
        tokens.push(path)
        path = ''
      }
  
      var partial = prefix != null && next != null && next !== prefix
      var repeat = modifier === '+' || modifier === '*'
      var optional = modifier === '?' || modifier === '*'
      var delimiter = res[2] || '/'
      var pattern = capture || group || (asterisk ? '.*' : '[^' + delimiter + ']+?')
  
      tokens.push({
        name: name || key++,
        prefix: prefix || '',
        delimiter: delimiter,
        optional: optional,
        repeat: repeat,
        partial: partial,
        asterisk: !!asterisk,
        pattern: escapeGroup(pattern)
      })
    }
  
    // Match any characters still remaining.
    if (index < str.length) {
      path += str.substr(index)
    }
  
    // If the path exists, push it onto the end.
    if (path) {
      tokens.push(path)
    }
  
    return tokens
  }
  
  /**
   * Compile a string to a template function for the path.
   *
   * @param  {string}             str
   * @return {!function(Object=, Object=)}
   */
  function compile (str) {
    return tokensToFunction(parse(str))
  }
  
  /**
   * Prettier encoding of URI path segments.
   *
   * @param  {string}
   * @return {string}
   */
  function encodeURIComponentPretty (str) {
    return encodeURI(str).replace(/[\/?#]/g, function (c) {
      return '%' + c.charCodeAt(0).toString(16).toUpperCase()
    })
  }
  
  /**
   * Encode the asterisk parameter. Similar to `pretty`, but allows slashes.
   *
   * @param  {string}
   * @return {string}
   */
  function encodeAsterisk (str) {
    return encodeURI(str).replace(/[?#]/g, function (c) {
      return '%' + c.charCodeAt(0).toString(16).toUpperCase()
    })
  }
  
  /**
   * Expose a method for transforming tokens into the path function.
   */
  function tokensToFunction (tokens) {
    // Compile all the tokens into regexps.
    var matches = new Array(tokens.length)
  
    // Compile all the patterns before compilation.
    for (var i = 0; i < tokens.length; i++) {
      if (typeof tokens[i] === 'object') {
        matches[i] = new RegExp('^(?:' + tokens[i].pattern + ')$')
      }
    }
  
    return function (obj, opts) {
      var path = ''
      var data = obj || {}
      var options = opts || {}
      var encode = options.pretty ? encodeURIComponentPretty : encodeURIComponent
  
      for (var i = 0; i < tokens.length; i++) {
        var token = tokens[i]
  
        if (typeof token === 'string') {
          path += token
  
          continue
        }
  
        var value = data[token.name]
        var segment
  
        if (value == null) {
          if (token.optional) {
            // Prepend partial segment prefixes.
            if (token.partial) {
              path += token.prefix
            }
  
            continue
          } else {
            throw new TypeError('Expected "' + token.name + '" to be defined')
          }
        }
  
        if (isarray(value)) {
          if (!token.repeat) {
            throw new TypeError('Expected "' + token.name + '" to not repeat, but received `' + JSON.stringify(value) + '`')
          }
  
          if (value.length === 0) {
            if (token.optional) {
              continue
            } else {
              throw new TypeError('Expected "' + token.name + '" to not be empty')
            }
          }
  
          for (var j = 0; j < value.length; j++) {
            segment = encode(value[j])
  
            if (!matches[i].test(segment)) {
              throw new TypeError('Expected all "' + token.name + '" to match "' + token.pattern + '", but received `' + JSON.stringify(segment) + '`')
            }
  
            path += (j === 0 ? token.prefix : token.delimiter) + segment
          }
  
          continue
        }
  
        segment = token.asterisk ? encodeAsterisk(value) : encode(value)
  
        if (!matches[i].test(segment)) {
          throw new TypeError('Expected "' + token.name + '" to match "' + token.pattern + '", but received "' + segment + '"')
        }
  
        path += token.prefix + segment
      }
  
      return path
    }
  }
  
  /**
   * Escape a regular expression string.
   *
   * @param  {string} str
   * @return {string}
   */
  function escapeString (str) {
    return str.replace(/([.+*?=^!:${}()[\]|\/\\])/g, '\\$1')
  }
  
  /**
   * Escape the capturing group by escaping special characters and meaning.
   *
   * @param  {string} group
   * @return {string}
   */
  function escapeGroup (group) {
    return group.replace(/([=!:$\/()])/g, '\\$1')
  }
  
  /**
   * Attach the keys as a property of the regexp.
   *
   * @param  {!RegExp} re
   * @param  {Array}   keys
   * @return {!RegExp}
   */
  function attachKeys (re, keys) {
    re.keys = keys
    return re
  }
  
  /**
   * Get the flags for a regexp from the options.
   *
   * @param  {Object} options
   * @return {string}
   */
  function flags (options) {
    return options.sensitive ? '' : 'i'
  }
  
  /**
   * Pull out keys from a regexp.
   *
   * @param  {!RegExp} path
   * @param  {!Array}  keys
   * @return {!RegExp}
   */
  function regexpToRegexp (path, keys) {
    // Use a negative lookahead to match only capturing groups.
    var groups = path.source.match(/\((?!\?)/g)
  
    if (groups) {
      for (var i = 0; i < groups.length; i++) {
        keys.push({
          name: i,
          prefix: null,
          delimiter: null,
          optional: false,
          repeat: false,
          partial: false,
          asterisk: false,
          pattern: null
        })
      }
    }
  
    return attachKeys(path, keys)
  }
  
  /**
   * Transform an array into a regexp.
   *
   * @param  {!Array}  path
   * @param  {Array}   keys
   * @param  {!Object} options
   * @return {!RegExp}
   */
  function arrayToRegexp (path, keys, options) {
    var parts = []
  
    for (var i = 0; i < path.length; i++) {
      parts.push(pathToRegexp(path[i], keys, options).source)
    }
  
    var regexp = new RegExp('(?:' + parts.join('|') + ')', flags(options))
  
    return attachKeys(regexp, keys)
  }
  
  /**
   * Create a path regexp from string input.
   *
   * @param  {string}  path
   * @param  {!Array}  keys
   * @param  {!Object} options
   * @return {!RegExp}
   */
  function stringToRegexp (path, keys, options) {
    var tokens = parse(path)
    var re = tokensToRegExp(tokens, options)
  
    // Attach keys back to the regexp.
    for (var i = 0; i < tokens.length; i++) {
      if (typeof tokens[i] !== 'string') {
        keys.push(tokens[i])
      }
    }
  
    return attachKeys(re, keys)
  }
  
  /**
   * Expose a function for taking tokens and returning a RegExp.
   *
   * @param  {!Array}  tokens
   * @param  {Object=} options
   * @return {!RegExp}
   */
  function tokensToRegExp (tokens, options) {
    options = options || {}
  
    var strict = options.strict
    var end = options.end !== false
    var route = ''
    var lastToken = tokens[tokens.length - 1]
    var endsWithSlash = typeof lastToken === 'string' && /\/$/.test(lastToken)
  
    // Iterate over the tokens and create our regexp string.
    for (var i = 0; i < tokens.length; i++) {
      var token = tokens[i]
  
      if (typeof token === 'string') {
        route += escapeString(token)
      } else {
        var prefix = escapeString(token.prefix)
        var capture = '(?:' + token.pattern + ')'
  
        if (token.repeat) {
          capture += '(?:' + prefix + capture + ')*'
        }
  
        if (token.optional) {
          if (!token.partial) {
            capture = '(?:' + prefix + '(' + capture + '))?'
          } else {
            capture = prefix + '(' + capture + ')?'
          }
        } else {
          capture = prefix + '(' + capture + ')'
        }
  
        route += capture
      }
    }
  
    // In non-strict mode we allow a slash at the end of match. If the path to
    // match already ends with a slash, we remove it for consistency. The slash
    // is valid at the end of a path match, not in the middle. This is important
    // in non-ending mode, where "/test/" shouldn't match "/test//route".
    if (!strict) {
      route = (endsWithSlash ? route.slice(0, -2) : route) + '(?:\\/(?=$))?'
    }
  
    if (end) {
      route += '$'
    } else {
      // In non-ending mode, we need the capturing groups to match as much as
      // possible by using a positive lookahead to the end or next path segment.
      route += strict && endsWithSlash ? '' : '(?=\\/|$)'
    }
  
    return new RegExp('^' + route, flags(options))
  }
  
  /**
   * Normalize the given path string, returning a regular expression.
   *
   * An empty array can be passed in for the keys, which will hold the
   * placeholder key descriptions. For example, using `/user/:id`, `keys` will
   * contain `[{ name: 'id', delimiter: '/', optional: false, repeat: false }]`.
   *
   * @param  {(string|RegExp|Array)} path
   * @param  {(Array|Object)=}       keys
   * @param  {Object=}               options
   * @return {!RegExp}
   */
  function pathToRegexp (path, keys, options) {
    keys = keys || []
  
    if (!isarray(keys)) {
      options = /** @type {!Object} */ (keys)
      keys = []
    } else if (!options) {
      options = {}
    }
  
    if (path instanceof RegExp) {
      return regexpToRegexp(path, /** @type {!Array} */ (keys))
    }
  
    if (isarray(path)) {
      return arrayToRegexp(/** @type {!Array} */ (path), /** @type {!Array} */ (keys), options)
    }
  
    return stringToRegexp(/** @type {string} */ (path), /** @type {!Array} */ (keys), options)
  }
  
  index.parse = parse_1;
  index.compile = compile_1;
  index.tokensToFunction = tokensToFunction_1;
  index.tokensToRegExp = tokensToRegExp_1;
  
  /*  */
  
  function createRouteMap (routes) {
    var pathMap = Object.create(null)
    var nameMap = Object.create(null)
  
    routes.forEach(function (route) {
      addRouteRecord(pathMap, nameMap, route)
    })
  
    return {
      pathMap: pathMap,
      nameMap: nameMap
    }
  }
  
  function addRouteRecord (
    pathMap,
    nameMap,
    route,
    parent,
    matchAs
  ) {
    var path = route.path;
    var name = route.name;
    assert(path != null, "\"path\" is required in a route configuration.")
  
    var record = {
      path: normalizePath(path, parent),
      components: route.components || { default: route.component },
      instances: {},
      name: name,
      parent: parent,
      matchAs: matchAs,
      redirect: route.redirect,
      beforeEnter: route.beforeEnter,
      meta: route.meta || {}
    }
  
    if (route.children) {
      // Warn if route is named and has a default child route.
      // If users navigate to this route by name, the default child will
      // not be rendered (GH Issue #629)
      if ("production" !== 'production') {}
      route.children.forEach(function (child) {
        addRouteRecord(pathMap, nameMap, child, record)
      })
    }
  
    if (route.alias) {
      if (Array.isArray(route.alias)) {
        route.alias.forEach(function (alias) {
          addRouteRecord(pathMap, nameMap, { path: alias }, parent, record.path)
        })
      } else {
        addRouteRecord(pathMap, nameMap, { path: route.alias }, parent, record.path)
      }
    }
  
    pathMap[record.path] = record
    if (name) { nameMap[name] = record }
  }
  
  function normalizePath (path, parent) {
    path = path.replace(/\/$/, '')
    if (path[0] === '/') { return path }
    if (parent == null) { return path }
    return cleanPath(((parent.path) + "/" + path))
  }
  
  /*  */
  
  var regexpCache = Object.create(null)
  
  var regexpCompileCache = Object.create(null)
  
  function createMatcher (routes) {
    var ref = createRouteMap(routes);
    var pathMap = ref.pathMap;
    var nameMap = ref.nameMap;
  
    function match (
      raw,
      currentRoute,
      redirectedFrom
    ) {
      var location = normalizeLocation(raw, currentRoute)
      var name = location.name;
  
      if (name) {
        var record = nameMap[name]
        if (record) {
          location.path = fillParams(record.path, location.params, ("named route \"" + name + "\""))
          return _createRoute(record, location, redirectedFrom)
        }
      } else if (location.path) {
        location.params = {}
        for (var path in pathMap) {
          if (matchRoute(path, location.params, location.path)) {
            return _createRoute(pathMap[path], location, redirectedFrom)
          }
        }
      }
      // no match
      return _createRoute(null, location)
    }
  
    function redirect (
      record,
      location
    ) {
      var originalRedirect = record.redirect
      var redirect = typeof originalRedirect === 'function'
          ? originalRedirect(createRoute(record, location))
          : originalRedirect
  
      if (typeof redirect === 'string') {
        redirect = { path: redirect }
      }
  
      if (!redirect || typeof redirect !== 'object') {
        warn(false, ("invalid redirect option: " + (JSON.stringify(redirect))))
        return _createRoute(null, location)
      }
  
      var re = redirect
      var name = re.name;
      var path = re.path;
      var query = location.query;
      var hash = location.hash;
      var params = location.params;
      query = re.hasOwnProperty('query') ? re.query : query
      hash = re.hasOwnProperty('hash') ? re.hash : hash
      params = re.hasOwnProperty('params') ? re.params : params
  
      if (name) {
        // resolved named direct
        var targetRecord = nameMap[name]
        assert(targetRecord, ("redirect failed: named route \"" + name + "\" not found."))
        return match({
          _normalized: true,
          name: name,
          query: query,
          hash: hash,
          params: params
        }, undefined, location)
      } else if (path) {
        // 1. resolve relative redirect
        var rawPath = resolveRecordPath(path, record)
        // 2. resolve params
        var resolvedPath = fillParams(rawPath, params, ("redirect route with path \"" + rawPath + "\""))
        // 3. rematch with existing query and hash
        return match({
          _normalized: true,
          path: resolvedPath,
          query: query,
          hash: hash
        }, undefined, location)
      } else {
        warn(false, ("invalid redirect option: " + (JSON.stringify(redirect))))
        return _createRoute(null, location)
      }
    }
  
    function alias (
      record,
      location,
      matchAs
    ) {
      var aliasedPath = fillParams(matchAs, location.params, ("aliased route with path \"" + matchAs + "\""))
      var aliasedMatch = match({
        _normalized: true,
        path: aliasedPath
      })
      if (aliasedMatch) {
        var matched = aliasedMatch.matched
        var aliasedRecord = matched[matched.length - 1]
        location.params = aliasedMatch.params
        return _createRoute(aliasedRecord, location)
      }
      return _createRoute(null, location)
    }
  
    function _createRoute (
      record,
      location,
      redirectedFrom
    ) {
      if (record && record.redirect) {
        return redirect(record, redirectedFrom || location)
      }
      if (record && record.matchAs) {
        return alias(record, location, record.matchAs)
      }
      return createRoute(record, location, redirectedFrom)
    }
  
    return match
  }
  
  function matchRoute (
    path,
    params,
    pathname
  ) {
    var keys, regexp
    var hit = regexpCache[path]
    if (hit) {
      keys = hit.keys
      regexp = hit.regexp
    } else {
      keys = []
      regexp = index(path, keys)
      regexpCache[path] = { keys: keys, regexp: regexp }
    }
    var m = pathname.match(regexp)
  
    if (!m) {
      return false
    } else if (!params) {
      return true
    }
  
    for (var i = 1, len = m.length; i < len; ++i) {
      var key = keys[i - 1]
      var val = typeof m[i] === 'string' ? decodeURIComponent(m[i]) : m[i]
      if (key) { params[key.name] = val }
    }
  
    return true
  }
  
  function fillParams (
    path,
    params,
    routeMsg
  ) {
    try {
      var filler =
        regexpCompileCache[path] ||
        (regexpCompileCache[path] = index.compile(path))
      return filler(params || {}, { pretty: true })
    } catch (e) {
      assert(false, ("missing param for " + routeMsg + ": " + (e.message)))
      return ''
    }
  }
  
  function resolveRecordPath (path, record) {
    return resolvePath(path, record.parent ? record.parent.path : '/', true)
  }
  
  /*  */
  
  var inBrowser = typeof window !== 'undefined'
  
  var supportsHistory = inBrowser && (function () {
    var ua = window.navigator.userAgent
  
    if (
      (ua.indexOf('Android 2.') !== -1 || ua.indexOf('Android 4.0') !== -1) &&
      ua.indexOf('Mobile Safari') !== -1 &&
      ua.indexOf('Chrome') === -1 &&
      ua.indexOf('Windows Phone') === -1
    ) {
      return false
    }
  
    return window.history && 'pushState' in window.history
  })()
  
  /*  */
  
  function runQueue (queue, fn, cb) {
    var step = function (index) {
      if (index >= queue.length) {
        cb()
      } else {
        if (queue[index]) {
          fn(queue[index], function () {
            step(index + 1)
          })
        } else {
          step(index + 1)
        }
      }
    }
    step(0)
  }
  
  /*  */
  
  
  var History = function History (router, base) {
    this.router = router
    this.base = normalizeBase(base)
    // start with a route object that stands for "nowhere"
    this.current = START
    this.pending = null
  };
  
  History.prototype.listen = function listen (cb) {
    this.cb = cb
  };
  
  History.prototype.transitionTo = function transitionTo (location, cb) {
      var this$1 = this;
  
    var route = this.router.match(location, this.current)
    this.confirmTransition(route, function () {
      this$1.updateRoute(route)
      cb && cb(route)
      this$1.ensureURL()
    })
  };
  
  History.prototype.confirmTransition = function confirmTransition (route, cb) {
      var this$1 = this;
  
    var current = this.current
    if (isSameRoute(route, current)) {
      this.ensureURL()
      return
    }
  
    var ref = resolveQueue(this.current.matched, route.matched);
      var deactivated = ref.deactivated;
      var activated = ref.activated;
  
    var queue = [].concat(
      // in-component leave guards
      extractLeaveGuards(deactivated),
      // global before hooks
      this.router.beforeHooks,
      // enter guards
      activated.map(function (m) { return m.beforeEnter; }),
      // async components
      resolveAsyncComponents(activated)
    )
  
    this.pending = route
    var iterator = function (hook, next) {
      if (this$1.pending !== route) { return }
      hook(route, current, function (to) {
        if (to === false) {
          // next(false) -> abort navigation, ensure current URL
          this$1.ensureURL()
        } else if (typeof to === 'string' || typeof to === 'object') {
          // next('/') or next({ path: '/' }) -> redirect
          this$1.push(to)
        } else {
          // confirm transition and pass on the value
          next(to)
        }
      })
    }
  
    runQueue(queue, iterator, function () {
      var postEnterCbs = []
      // wait until async components are resolved before
      // extracting in-component enter guards
      runQueue(extractEnterGuards(activated, postEnterCbs), iterator, function () {
        if (this$1.pending === route) {
          this$1.pending = null
          cb(route)
          this$1.router.app.$nextTick(function () {
            postEnterCbs.forEach(function (cb) { return cb(); })
          })
        }
      })
    })
  };
  
  History.prototype.updateRoute = function updateRoute (route) {
    var prev = this.current
    this.current = route
    this.cb && this.cb(route)
    this.router.afterHooks.forEach(function (hook) {
      hook && hook(route, prev)
    })
  };
  
  function normalizeBase (base) {
    if (!base) {
      if (inBrowser) {
        // respect <base> tag
        var baseEl = document.querySelector('base')
        base = baseEl ? baseEl.getAttribute('href') : '/'
      } else {
        base = '/'
      }
    }
    // make sure there's the starting slash
    if (base.charAt(0) !== '/') {
      base = '/' + base
    }
    // remove trailing slash
    return base.replace(/\/$/, '')
  }
  
  function resolveQueue (
    current,
    next
  ) {
    var i
    var max = Math.max(current.length, next.length)
    for (i = 0; i < max; i++) {
      if (current[i] !== next[i]) {
        break
      }
    }
    return {
      activated: next.slice(i),
      deactivated: current.slice(i)
    }
  }
  
  function extractLeaveGuards (matched) {
    return flatMapComponents(matched, function (def, instance) {
      var guard = def && def.beforeRouteLeave
      if (guard) {
        return function routeLeaveGuard () {
          return guard.apply(instance, arguments)
        }
      }
    }).reverse()
  }
  
  function extractEnterGuards (matched, cbs) {
    return flatMapComponents(matched, function (def, _, match, key) {
      var guard = def && def.beforeRouteEnter
      if (guard) {
        return function routeEnterGuard (to, from, next) {
          return guard(to, from, function (cb) {
            next(cb)
            if (typeof cb === 'function') {
              cbs.push(function () {
                cb(match.instances[key])
              })
            }
          })
        }
      }
    })
  }
  
  function resolveAsyncComponents (matched) {
    return flatMapComponents(matched, function (def, _, match, key) {
      // if it's a function and doesn't have Vue options attached,
      // assume it's an async component resolve function.
      // we are not using Vue's default async resolving mechanism because
      // we want to halt the navigation until the incoming component has been
      // resolved.
      if (typeof def === 'function' && !def.options) {
        return function (to, from, next) {
          var resolve = function (resolvedDef) {
            match.components[key] = resolvedDef
            next()
          }
  
          var reject = function (reason) {
            warn(false, ("Failed to resolve async component " + key + ": " + reason))
            next(false)
          }
  
          var res = def(resolve, reject)
          if (res && typeof res.then === 'function') {
            res.then(resolve, reject)
          }
        }
      }
    })
  }
  
  function flatMapComponents (
    matched,
    fn
  ) {
    return Array.prototype.concat.apply([], matched.map(function (m) {
      return Object.keys(m.components).map(function (key) { return fn(
        m.components[key],
        m.instances[key],
        m, key
      ); })
    }))
  }
  
  /*  */
  
  function saveScrollPosition (key) {
    if (!key) { return }
    window.sessionStorage.setItem(key, JSON.stringify({
      x: window.pageXOffset,
      y: window.pageYOffset
    }))
  }
  
  function getScrollPosition (key) {
    if (!key) { return }
    return JSON.parse(window.sessionStorage.getItem(key))
  }
  
  function getElementPosition (el) {
    var docRect = document.documentElement.getBoundingClientRect()
    var elRect = el.getBoundingClientRect()
    return {
      x: elRect.left - docRect.left,
      y: elRect.top - docRect.top
    }
  }
  
  function isValidPosition (obj) {
    return isNumber(obj.x) || isNumber(obj.y)
  }
  
  function normalizePosition (obj) {
    return {
      x: isNumber(obj.x) ? obj.x : window.pageXOffset,
      y: isNumber(obj.y) ? obj.y : window.pageYOffset
    }
  }
  
  function isNumber (v) {
    return typeof v === 'number'
  }
  
  /*  */
  
  
  var genKey = function () { return String(Date.now()); }
  var _key = genKey()
  
  var HTML5History = (function (History) {
    function HTML5History (router, base) {
      var this$1 = this;
  
      History.call(this, router, base)
  
      this.transitionTo(getLocation(this.base))
  
      var expectScroll = router.options.scrollBehavior
      window.addEventListener('popstate', function (e) {
        _key = e.state && e.state.key
        var current = this$1.current
        this$1.transitionTo(getLocation(this$1.base), function (next) {
          if (expectScroll) {
            this$1.handleScroll(next, current, true)
          }
        })
      })
  
      if (expectScroll) {
        window.addEventListener('scroll', function () {
          saveScrollPosition(_key)
        })
      }
    }
  
    if ( History ) HTML5History.__proto__ = History;
    HTML5History.prototype = Object.create( History && History.prototype );
    HTML5History.prototype.constructor = HTML5History;
  
    HTML5History.prototype.go = function go (n) {
      window.history.go(n)
    };
  
    HTML5History.prototype.push = function push (location) {
      var this$1 = this;
  
      var current = this.current
      this.transitionTo(location, function (route) {
        pushState(cleanPath(this$1.base + route.fullPath))
        this$1.handleScroll(route, current, false)
      })
    };
  
    HTML5History.prototype.replace = function replace (location) {
      var this$1 = this;
  
      var current = this.current
      this.transitionTo(location, function (route) {
        replaceState(cleanPath(this$1.base + route.fullPath))
        this$1.handleScroll(route, current, false)
      })
    };
  
    HTML5History.prototype.ensureURL = function ensureURL () {
      if (getLocation(this.base) !== this.current.fullPath) {
        replaceState(cleanPath(this.base + this.current.fullPath))
      }
    };
  
    HTML5History.prototype.handleScroll = function handleScroll (to, from, isPop) {
      var router = this.router
      if (!router.app) {
        return
      }
  
      var behavior = router.options.scrollBehavior
      if (!behavior) {
        return
      }
      assert(typeof behavior === 'function', "scrollBehavior must be a function")
  
      // wait until re-render finishes before scrolling
      router.app.$nextTick(function () {
        var position = getScrollPosition(_key)
        var shouldScroll = behavior(to, from, isPop ? position : null)
        if (!shouldScroll) {
          return
        }
        var isObject = typeof shouldScroll === 'object'
        if (isObject && typeof shouldScroll.selector === 'string') {
          var el = document.querySelector(shouldScroll.selector)
          if (el) {
            position = getElementPosition(el)
          } else if (isValidPosition(shouldScroll)) {
            position = normalizePosition(shouldScroll)
          }
        } else if (isObject && isValidPosition(shouldScroll)) {
          position = normalizePosition(shouldScroll)
        }
  
        if (position) {
          window.scrollTo(position.x, position.y)
        }
      })
    };
  
    return HTML5History;
  }(History));
  
  function getLocation (base) {
    var path = window.location.pathname
    if (base && path.indexOf(base) === 0) {
      path = path.slice(base.length)
    }
    return (path || '/') + window.location.search + window.location.hash
  }
  
  function pushState (url, replace) {
    // try...catch the pushState call to get around Safari
    // DOM Exception 18 where it limits to 100 pushState calls
    var history = window.history
    try {
      if (replace) {
        history.replaceState({ key: _key }, '', url)
      } else {
        _key = genKey()
        history.pushState({ key: _key }, '', url)
      }
      saveScrollPosition(_key)
    } catch (e) {
      window.location[replace ? 'assign' : 'replace'](url)
    }
  }
  
  function replaceState (url) {
    pushState(url, true)
  }
  
  /*  */
  
  
  var HashHistory = (function (History) {
    function HashHistory (router, base, fallback) {
      var this$1 = this;
  
      History.call(this, router, base)
  
      // check history fallback deeplinking
      if (fallback && this.checkFallback()) {
        return
      }
  
      ensureSlash()
      this.transitionTo(getHash())
  
      window.addEventListener('hashchange', function () {
        this$1.onHashChange()
      })
    }
  
    if ( History ) HashHistory.__proto__ = History;
    HashHistory.prototype = Object.create( History && History.prototype );
    HashHistory.prototype.constructor = HashHistory;
  
    HashHistory.prototype.checkFallback = function checkFallback () {
      var location = getLocation(this.base)
      if (!/^\/#/.test(location)) {
        window.location.replace(
          cleanPath(this.base + '/#' + location)
        )
        return true
      }
    };
  
    HashHistory.prototype.onHashChange = function onHashChange () {
      if (!ensureSlash()) {
        return
      }
      this.transitionTo(getHash(), function (route) {
        replaceHash(route.fullPath)
      })
    };
  
    HashHistory.prototype.push = function push (location) {
      History.prototype.transitionTo.call(this, location, function (route) {
        pushHash(route.fullPath)
      })
    };
  
    HashHistory.prototype.replace = function replace (location) {
      History.prototype.transitionTo.call(this, location, function (route) {
        replaceHash(route.fullPath)
      })
    };
  
    HashHistory.prototype.go = function go (n) {
      window.history.go(n)
    };
  
    HashHistory.prototype.ensureURL = function ensureURL () {
      if (getHash() !== this.current.fullPath) {
        replaceHash(this.current.fullPath)
      }
    };
  
    return HashHistory;
  }(History));
  
  function ensureSlash () {
    var path = getHash()
    if (path.charAt(0) === '/') {
      return true
    }
    replaceHash('/' + path)
    return false
  }
  
  function getHash () {
    // We can't use window.location.hash here because it's not
    // consistent across browsers - Firefox will pre-decode it!
    var href = window.location.href
    var index = href.indexOf('#')
    return index === -1 ? '' : href.slice(index + 1)
  }
  
  function pushHash (path) {
    window.location.hash = path
  }
  
  function replaceHash (path) {
    var i = window.location.href.indexOf('#')
    window.location.replace(
      window.location.href.slice(0, i >= 0 ? i : 0) + '#' + path
    )
  }
  
  /*  */
  
  
  var AbstractHistory = (function (History) {
    function AbstractHistory (router) {
      History.call(this, router)
      this.stack = []
      this.index = 0
    }
  
    if ( History ) AbstractHistory.__proto__ = History;
    AbstractHistory.prototype = Object.create( History && History.prototype );
    AbstractHistory.prototype.constructor = AbstractHistory;
  
    AbstractHistory.prototype.push = function push (location) {
      var this$1 = this;
  
      History.prototype.transitionTo.call(this, location, function (route) {
        this$1.stack = this$1.stack.slice(0, this$1.index + 1).concat(route)
        this$1.index++
      })
    };
  
    AbstractHistory.prototype.replace = function replace (location) {
      var this$1 = this;
  
      History.prototype.transitionTo.call(this, location, function (route) {
        this$1.stack = this$1.stack.slice(0, this$1.index).concat(route)
      })
    };
  
    AbstractHistory.prototype.go = function go (n) {
      var this$1 = this;
  
      var targetIndex = this.index + n
      if (targetIndex < 0 || targetIndex >= this.stack.length) {
        return
      }
      var location = this.stack[targetIndex]
      this.confirmTransition(location, function () {
        this$1.index = targetIndex
        this$1.updateRoute(location)
      })
    };
  
    AbstractHistory.prototype.ensureURL = function ensureURL () {
      // noop
    };
  
    return AbstractHistory;
  }(History));
  
  /*  */
  
  var VueRouter = function VueRouter (options) {
    if ( options === void 0 ) options = {};
  
    this.app = null
    this.options = options
    this.beforeHooks = []
    this.afterHooks = []
    this.match = createMatcher(options.routes || [])
  
    var mode = options.mode || 'hash'
    this.fallback = mode === 'history' && !supportsHistory
    if (this.fallback) {
      mode = 'hash'
    }
    if (!inBrowser) {
      mode = 'abstract'
    }
    this.mode = mode
  };
  
  var prototypeAccessors = { currentRoute: {} };
  
  prototypeAccessors.currentRoute.get = function () {
    return this.history && this.history.current
  };
  
  VueRouter.prototype.init = function init (app /* Vue component instance */) {
      var this$1 = this;
  
    assert(
      install.installed,
      "not installed. Make sure to call `Vue.use(VueRouter)` " +
      "before creating root instance."
    )
  
    this.app = app
  
    var ref = this;
      var mode = ref.mode;
      var options = ref.options;
      var fallback = ref.fallback;
    switch (mode) {
      case 'history':
        this.history = new HTML5History(this, options.base)
        break
      case 'hash':
        this.history = new HashHistory(this, options.base, fallback)
        break
      case 'abstract':
        this.history = new AbstractHistory(this)
        break
      default:
        assert(false, ("invalid mode: " + mode))
    }
  
    this.history.listen(function (route) {
      this$1.app._route = route
    })
  };
  
  VueRouter.prototype.beforeEach = function beforeEach (fn) {
    this.beforeHooks.push(fn)
  };
  
  VueRouter.prototype.afterEach = function afterEach (fn) {
    this.afterHooks.push(fn)
  };
  
  VueRouter.prototype.push = function push (location) {
    this.history.push(location)
  };
  
  VueRouter.prototype.replace = function replace (location) {
    this.history.replace(location)
  };
  
  VueRouter.prototype.go = function go (n) {
    this.history.go(n)
  };
  
  VueRouter.prototype.back = function back () {
    this.go(-1)
  };
  
  VueRouter.prototype.forward = function forward () {
    this.go(1)
  };
  
  VueRouter.prototype.getMatchedComponents = function getMatchedComponents () {
    if (!this.currentRoute) {
      return []
    }
    return [].concat.apply([], this.currentRoute.matched.map(function (m) {
      return Object.keys(m.components).map(function (key) {
        return m.components[key]
      })
    }))
  };
  
  Object.defineProperties( VueRouter.prototype, prototypeAccessors );
  
  VueRouter.install = install
  
  if (inBrowser && window.Vue) {
    window.Vue.use(VueRouter)
  }
  
  return VueRouter;
  
  })));